package org.etnaframework.module.jdbc;

import org.etnaframework.module.base.logback.FileNameAndMethodNameConverter;
import org.etnaframework.module.base.utils.DatetimeUtils;
import org.etnaframework.module.base.utils.DbMap;
import org.etnaframework.module.base.utils.StackTraceUtils;
import org.etnaframework.module.jdbc.JdbcRowMapper.ColumnJdbcMapRowMapper;
import org.etnaframework.module.jdbc.exception.SqlExecuteException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanInstantiationException;
import org.springframework.jdbc.core.PreparedStatementCreator;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.RowMapperResultSetExtractor;
import org.springframework.lang.NonNull;

import javax.sql.DataSource;
import java.sql.*;
import java.util.*;
import java.util.Date;

/**
 * 数据库对象基本操作模板，基于Spring的JdbcTemplate根据业务需要改造而来
 *
 * @author jasyaf
 * @since 2023-12-27
 */
public class JdbcTemplate {

    static {
        FileNameAndMethodNameConverter.addIgnoreForLogback(JdbcTemplate.class);
        StackTraceUtils.addIgnorePrefix(JdbcTemplate.class);
    }

    protected final DataSource dataSource;

    protected final Logger log = LoggerFactory.getLogger(getClass());

    protected final org.springframework.jdbc.core.JdbcTemplate jdbcTemplate;

    private final JdbcDialectEnum dialect;

    public JdbcTemplate(DataSource dataSource, JdbcDialectEnum dialect) {
        this.jdbcTemplate = new org.springframework.jdbc.core.JdbcTemplate(dataSource);
        this.dataSource = dataSource;
        this.dialect = dialect;
    }

    public DataSource getDataSource() {
        return dataSource;
    }

    public SqlBuilder newSqlBuilder() {
        return new SqlBuilder(dialect.getQuoteString());
    }

    public SqlBuilder newSqlBuilder(String sql) {
        return new SqlBuilder(dialect.getQuoteString()).add(sql);
    }

    public SqlBuilder newSqlBuilder(SqlBuilder sql) {
        return new SqlBuilder(dialect.getQuoteString()).add(sql);
    }

    /**
     * 使用传入的class的字段生成 select 字段名 的sql
     */
    public SqlBuilder newSqlBuilderSelect(Class<?> clazz) {
        return new SqlBuilder(dialect.getQuoteString()).addSelectBean(null, clazz);
    }

    /**
     * 使用传入的class的字段生成 select 字段名 的sql
     *
     * @param tableName 表名，用于添加类似 t.id 的sql，如果传入null或空字符串就不生成表名字段
     */
    public SqlBuilder newSqlBuilderSelect(String tableName, Class<?> clazz) {
        return new SqlBuilder(dialect.getQuoteString()).addSelectBean(tableName, clazz);
    }

    /**
     * 执行insert操作，并获取到自增主键的值（如果想返回影响的行数，请使用update）
     *
     * @param sql  插入sql
     * @param args 插入sql语句中的?参数的值
     * @return 如果能获取到，将返回自增主键的值，否则返回-1
     */
    public long insert(String sql, Object... args) {
        long id = -1L;
        Throwable ex = null;
        String logString = null;
        long start = System.currentTimeMillis();
        int i = 0;
        try {
            id = jdbcTemplate.execute(new SqlCreatorForInsert(sql, args, dialect), ps -> {
                ps.execute();
                try { // 没有自增主键的表，下面的执行会抛出异常，返回-1，此时就不能根据返回值来判断是否添加成功了
                    ResultSet rs = ps.getGeneratedKeys(); // 获取自增主键
                    if (rs.next()) {
                        return rs.getLong(1);
                    }
                } catch (SQLException ignore) {
                }
                return -1L;
            });
            return id;
        } catch (Throwable e) {
            ex = e;
            logString = buildLog(sql, args) + " -- " + (System.currentTimeMillis() - start) + "ms-retry: " + i;
            throw new SqlExecuteException(logString, e);
        } finally {
            if (log.isDebugEnabled()) {
                if (null == ex) {
                    logString = buildLog(sql, args) + " -- " + (System.currentTimeMillis() - start) + "ms-result: " + id;
                    log.debug(logString);
                } else {
                    logString += " -- " + StackTraceUtils.printThrowable(ex);
                    log.error(logString);
                }
            }
        }
    }

    /**
     * 执行insert操作，并获取到自增主键的值（如果想返回影响的行数，请使用update）
     *
     * @param sql  插入sql
     * @param args 插入sql语句中的?参数的值
     * @return 如果能获取到，将返回自增主键的值，否则返回-1
     */
    public long insert(String sql, Collection<Object> args) {
        return insert(sql, args.toArray());
    }

    /**
     * 执行insert操作，并获取到自增主键的值（如果想返回影响的行数，请使用update）
     *
     * @param sql 插入sql以及其参数
     * @return 如果能获取到，将返回自增主键的值，否则返回-1
     */
    public long insert(SqlBuilder sql) {
        return insert(sql.sql.toString(), sql.args);
    }

    /**
     * 将一个javabean生成sql插入到数据库，通过反射获取到bean的所有非空字段自动生成sql
     *
     * @param prefixSql sql的前半部分，如insert into tablename，后面的values一段将由程序自动生成（mysql将使用set xx=aa, yy=bb的语法）
     * @param bean      预备插入的javabean，其中只要是非null的字段，都会参与到sql的生成，如果不想某个字段参与生成，请在字段上加@{@link DbField}设置
     * @return 如果能获取到，将返回自增主键的值，否则返回-1
     */
    public long insertOne(String prefixSql, Object bean) {
        SqlBuilder sql = newSqlBuilder(prefixSql);
        // 如果是mysql就使用set xx=aa, yy=bb这样的方式来insert，这样在日志中显得更直观
        if (JdbcDialectEnum.MySQL.equals(dialect)) {
            sql.add("set").addEqualsBean(bean);
        } else {
            sql.addInsertBean(bean);
        }
        return insert(sql);
    }

    /**
     * 将一个Map<String,Object>生成sql插入到数据库，所有非空value自动生成sql
     *
     * @param prefixSql sql的前半部分，如insert into tablename，后面的values一段将由程序自动生成（mysql将使用set xx=aa, yy=bb的语法）
     * @param map       预备插入的Map<String,Object>，其中只要是非null的value，都会参与到sql的生成
     * @return 如果能获取到，将返回自增主键的值，否则返回-1
     */
    public long insertOne(String prefixSql, Map<String, Object> map) {
        SqlBuilder sql = newSqlBuilder(prefixSql);
        // 如果是mysql就使用set xx=aa, yy=bb这样的方式来insert，这样在日志中显得更直观
        if (JdbcDialectEnum.MySQL.equals(dialect)) {
            sql.add("set").addEqualsBean(map);
        } else {
            sql.addInsertBean(map);
        }
        return insert(sql);
    }

    /**
     * 用于执行delete/update操作，返回的int代表该操作影响了多少行数据
     */
    public int update(String sql, Object... args) {
        int affected = 0;
        Throwable ex = null;
        String logString = null;
        long start = System.currentTimeMillis();
        int i = 0;
        try {
            affected = jdbcTemplate.update(new SqlCreator(sql, args, dialect));
            return affected;
        } catch (Throwable e) {
            ex = e;
            logString = buildLog(sql, args) + " -- " + (System.currentTimeMillis() - start) + "ms-retry: " + i;
            throw new SqlExecuteException(logString, e);
        } finally {
            if (log.isDebugEnabled()) {
                if (null == ex) {
                    logString = buildLog(sql, args) + " -- " + (System.currentTimeMillis() - start) + "ms-affected: " + affected;
                    log.debug(logString);
                } else {
                    logString += " -- " + StackTraceUtils.printThrowable(ex);
                    log.error(logString);
                }
            }
        }
    }

    /**
     * 用于执行delete/update操作，返回的int代表该操作影响了多少行数据
     */
    public int update(String sql, List<Object> args) {
        return update(sql, args.toArray());
    }

    /**
     * 用于执行delete/update操作，返回的int代表该操作影响了多少行数据
     */
    public int update(SqlBuilder sql) {
        return update(sql.sql.toString(), sql.args);
    }

    /**
     * 用于批量执行delete/update操作（目前仍是一条条执行，稍候要修改成真正的批量更新）
     */
    public void batchUpdate(String sql, List<Object[]> args) {
        for (Object[] objs : args) {
            update(sql, objs);
        }
    }

    /**
     * 用于批量执行delete/update操作
     */
    public void batchUpdate(List<SqlBuilder> list) {
        for (SqlBuilder sql : list) {
            update(sql);
        }
    }

    /**
     * 将一个javabean生成sql更新到数据库，通过反射获取到bean的所有非空字段自动生成sql
     *
     * @param prefixSql sql的前半部分，如update tablename set，后面的XX=aa等键值对将由程序自动生成
     * @param bean      预备插入的javabean，其中只要是非null的字段，都会参与到sql的生成<br/>
     *                  ●如果不想某个字段参与生成， 请在字段上加注解@{@link DbField}并设置@{@link DbField#writeToDb()}=false<br/>
     *                  ●如果想让null值的字段参与生成，请在字段上加注解@{@link DbField}并设置@{@link DbField#writeNullToDb()}=true
     * @param suffixSql sql尾部的where部分，如where userId=xxx
     * @return 返回影响的数据行数，一般>1
     */
    public int updateOne(String prefixSql, Object bean, String suffixSql, Object... args) {
        SqlBuilder sql = newSqlBuilder(prefixSql);
        sql.addEqualsBean(bean);
        sql.add(suffixSql, args);
        return update(sql);
    }

    /**
     * 将一个javabean生成sql更新到数据库，通过反射获取到bean的所有非空字段自动生成sql
     *
     * @param prefixSql sql的前半部分，如update tablename set，后面的XX=aa等键值对将由程序自动生成
     * @param bean      预备更新的javabean，其中只要是非null的字段，都会参与到sql的生成<br/>
     *                  ●如果不想某个字段参与生成， 请在字段上加注解@{@link DbField}并设置@{@link DbField#writeToDb()}=false<br/>
     *                  ●如果想让null值的字段参与生成，请在字段上加注解@{@link DbField}并设置@{@link DbField#writeNullToDb()}=true
     * @param suffixSql sql尾部的where部分，如where userId=xxx
     * @return 返回影响的数据行数，一般>1
     */
    public int updateOne(String prefixSql, Object bean, String suffixSql, List<Object> args) {
        SqlBuilder sql = newSqlBuilder(prefixSql);
        sql.addEqualsBean(bean);
        sql.add(suffixSql, args);
        return update(sql);
    }

    /**
     * 将一个Map<String,Object>生成sql更新到数据库，所有非空value自动生成sql
     *
     * @param prefixSql sql的前半部分，如insert into tablename，后面的values一段将由程序自动生成
     * @param map       预备插入的Map<String,Object>，其中只要是非null的value，都会参与到sql的生成
     * @param suffixSql sql尾部的where部分，如where userId=xxx
     * @return 返回影响的数据行数，一般>1
     */
    public int updateOne(String prefixSql, Map<String, Object> map, String suffixSql, Object... args) {
        SqlBuilder sql = newSqlBuilder(prefixSql);
        sql.addEqualsBean(map);
        sql.add(suffixSql, args);
        return update(sql);
    }

    /**
     * 将一个Map<String,Object>生成sql更新到数据库，所有非空value自动生成sql
     *
     * @param prefixSql sql的前半部分，如insert into tablename，后面的values一段将由程序自动生成
     * @param map       预备插入的Map<String,Object>，其中只要是非null的value，都会参与到sql的生成
     * @param suffixSql sql尾部的where部分，如where userId=xxx
     * @return 返回影响的数据行数，一般>1
     */
    public int updateOne(String prefixSql, Map<String, Object> map, String suffixSql, List<Object> args) {
        SqlBuilder sql = newSqlBuilder(prefixSql);
        sql.addEqualsBean(map);
        sql.add(suffixSql, args);
        return update(sql);
    }

    /**
     * 查询并获取到一个{@link Boolean}
     *
     * @param sql  查询sql
     * @param args 查询sql语句中的?参数的值
     * @return 如果没有查到对应的记录将返回null
     */
    public Boolean queryBool(String sql, Object... args) {
        String bool = queryOne(String.class, sql, args);
        if (null == bool) {
            return null;
        }
        String val = ((Object) bool).toString();
        return val.equals("true") || val.equalsIgnoreCase("y") || val.equals("1") || val.equals("是");
    }

    /**
     * 查询并获取到一个{@link Boolean}
     *
     * @param sql  查询sql
     * @param args 查询sql语句中的?参数的值
     * @return 如果没有查到对应的记录将返回null
     */
    public Boolean queryBool(String sql, List<Object> args) {
        return queryBool(sql, args.toArray());
    }

    /**
     * 查询并获取到一个{@link Boolean}
     *
     * @param sql 查询sql以及其参数
     * @return 如果没有查到对应的记录将返回null
     */
    public Boolean queryBool(SqlBuilder sql) {
        return queryBool(sql.sql.toString(), sql.args);
    }

    /**
     * 查询并获取到一个{@link Boolean}
     *
     * @param defaultValue 当没有查到结果时，默认返回值
     * @param sql          查询sql
     * @param args         查询sql语句中的?参数的值
     * @return 如果没有查到对应的记录将返回defaultValue
     */
    public Boolean queryBoolOrDefault(Boolean defaultValue, String sql, Object... args) {
        String bool = queryOne(String.class, sql, args);
        if (null == bool) {
            return defaultValue;
        }
        String val = ((Object) bool).toString();
        return val.equals("true") || val.equalsIgnoreCase("y") || val.equals("1") || val.equals("是");
    }

    /**
     * 查询并获取到一个{@link Boolean}
     *
     * @param defaultValue 当没有查到结果时，默认返回值*
     * @param sql          查询sql
     * @param args         查询sql语句中的?参数的值
     * @return 如果没有查到对应的记录将返回defaultValue
     */
    public Boolean queryBoolOrDefault(Boolean defaultValue, String sql, List<Object> args) {
        return queryBoolOrDefault(defaultValue, sql, args.toArray());
    }

    /**
     * 查询并获取到一个{@link Boolean}
     *
     * @param defaultValue 当没有查到结果时，默认返回值
     * @param sql          查询sql以及其参数
     * @return 如果没有查到对应的记录将返回defaultValue
     */
    public Boolean queryBoolOrDefault(Boolean defaultValue, SqlBuilder sql) {
        return queryBoolOrDefault(defaultValue, sql.sql.toString(), sql.args);
    }

    /**
     * 查询并获取到一个{@link Byte}
     *
     * @param sql  查询sql
     * @param args 查询sql语句中的?参数的值
     * @return 如果没有查到对应的记录将返回null
     */
    public Byte queryByte(String sql, Object... args) {
        return queryOne(Byte.class, sql, args);
    }

    /**
     * 查询并获取到一个{@link Byte}
     *
     * @param sql  查询sql
     * @param args 查询sql语句中的?参数的值
     * @return 如果没有查到对应的记录将返回null
     */
    public Byte queryByte(String sql, List<Object> args) {
        return queryByte(sql, args.toArray());
    }

    /**
     * 查询并获取到一个{@link Byte}
     *
     * @param sql 查询sql以及其参数
     * @return 如果没有查到对应的记录将返回null
     */
    public Byte queryByte(SqlBuilder sql) {
        return queryByte(sql.sql.toString(), sql.args);
    }

    /**
     * 查询并获取到一个{@link Byte}
     *
     * @param defaultValue 当没有查到结果时，默认返回值
     * @param sql          查询sql
     * @param args         查询sql语句中的?参数的值
     * @return 如果没有查到对应的记录将返回defaultValue
     */
    public Byte queryByteOrDefault(Byte defaultValue, String sql, Object... args) {
        return queryOneOrDefault(defaultValue, Byte.class, sql, args);
    }

    /**
     * 查询并获取到一个{@link Byte}
     *
     * @param defaultValue 当没有查到结果时，默认返回值
     * @param sql          查询sql
     * @param args         查询sql语句中的?参数的值
     * @return 如果没有查到对应的记录将返回defaultValue
     */
    public Byte queryByteOrDefault(Byte defaultValue, String sql, List<Object> args) {
        return queryByteOrDefault(defaultValue, sql, args.toArray());
    }

    /**
     * 查询并获取到一个{@link Byte}
     *
     * @param defaultValue 当没有查到结果时，默认返回值
     * @param sql          查询sql以及其参数
     * @return 如果没有查到对应的记录将返回defaultValue
     */
    public Byte queryByteOrDefault(Byte defaultValue, SqlBuilder sql) {
        return queryByteOrDefault(defaultValue, sql.sql.toString(), sql.args);
    }

    /**
     * 查询并获取到一个{@link Short}
     *
     * @param sql  查询sql
     * @param args 查询sql语句中的?参数的值
     * @return 如果没有查到对应的记录将返回null
     */
    public Short queryShort(String sql, Object... args) {
        return queryOne(Short.class, sql, args);
    }

    /**
     * 查询并获取到一个{@link Short}
     *
     * @param sql  查询sql
     * @param args 查询sql语句中的?参数的值
     * @return 如果没有查到对应的记录将返回null
     */
    public Short queryShort(String sql, List<Object> args) {
        return queryShort(sql, args.toArray());
    }

    /**
     * 查询并获取到一个{@link Short}
     *
     * @param sql 查询sql以及其参数
     * @return 如果没有查到对应的记录将返回null
     */
    public Short queryShort(SqlBuilder sql) {
        return queryShort(sql.sql.toString(), sql.args);
    }

    /**
     * 查询并获取到一个{@link Short}
     *
     * @param sql 查询sql以及其参数
     * @return 如果没有查到对应的记录将返回null
     */
    public Short queryShort(String prefixSql, SqlBuilder sql) {
        return queryShort(newSqlBuilder(prefixSql).add(sql));
    }

    /**
     * 查询并获取到一个{@link Short}
     *
     * @param defaultValue 当没有查到结果时，默认返回值
     * @param sql          查询sql
     * @param args         查询sql语句中的?参数的值
     * @return 如果没有查到对应的记录将返回defaultValue
     */
    public Short queryShortOrDefault(Short defaultValue, String sql, Object... args) {
        return queryOneOrDefault(defaultValue, Short.class, sql, args);
    }

    /**
     * 查询并获取到一个{@link Short}
     *
     * @param defaultValue 当没有查到结果时，默认返回值
     * @param sql          查询sql
     * @param args         查询sql语句中的?参数的值
     * @return 如果没有查到对应的记录将返回defaultValue
     */
    public Short queryShortOrDefault(Short defaultValue, String sql, List<Object> args) {
        return queryShortOrDefault(defaultValue, sql, args.toArray());
    }

    /**
     * 查询并获取到一个{@link Short}
     *
     * @param defaultValue 当没有查到结果时，默认返回值
     * @param sql          查询sql以及其参数
     * @return 如果没有查到对应的记录将返回defaultValue
     */
    public Short queryShortOrDefault(Short defaultValue, SqlBuilder sql) {
        return queryShortOrDefault(defaultValue, sql.sql.toString(), sql.args);
    }

    /**
     * 查询并获取到一个{@link Short}
     *
     * @param defaultValue 当没有查到结果时，默认返回值
     * @param sql          查询sql以及其参数
     * @return 如果没有查到对应的记录将返回defaultValue
     */
    public Short queryShortOrDefault(Short defaultValue, String prefixSql, SqlBuilder sql) {
        return queryShortOrDefault(defaultValue, newSqlBuilder(prefixSql).add(sql));
    }

    /**
     * 查询并获取到一个{@link Integer}
     *
     * @param sql  查询sql
     * @param args 查询sql语句中的?参数的值
     * @return 如果没有查到对应的记录将返回null
     */
    public Integer queryInt(String sql, Object... args) {
        return queryOne(Integer.class, sql, args);
    }

    /**
     * 查询并获取到一个{@link Integer}
     *
     * @param sql  查询sql
     * @param args 查询sql语句中的?参数的值
     * @return 如果没有查到对应的记录将返回null
     */
    public Integer queryInt(String sql, List<Object> args) {
        return queryInt(sql, args.toArray());
    }

    /**
     * 查询并获取到一个{@link Integer}
     *
     * @param sql 查询sql以及其参数
     * @return 如果没有查到对应的记录将返回null
     */
    public Integer queryInt(SqlBuilder sql) {
        return queryInt(sql.sql.toString(), sql.args);
    }

    /**
     * 查询并获取到一个{@link Integer}
     *
     * @param sql 查询sql以及其参数
     * @return 如果没有查到对应的记录将返回null
     */
    public Integer queryInt(String prefixSql, SqlBuilder sql) {
        return queryInt(newSqlBuilder(prefixSql).add(sql));
    }

    /**
     * 查询并获取到一个{@link Integer}
     *
     * @param defaultValue 当没有查到结果时，默认返回值
     * @param sql          查询sql
     * @param args         查询sql语句中的?参数的值
     * @return 如果没有查到对应的记录将返回defaultValue
     */
    public Integer queryIntOrDefault(Integer defaultValue, String sql, Object... args) {
        return queryOneOrDefault(defaultValue, Integer.class, sql, args);
    }

    /**
     * 查询并获取到一个{@link Integer}
     *
     * @param defaultValue 当没有查到结果时，默认返回值
     * @param sql          查询sql
     * @param args         查询sql语句中的?参数的值
     * @return 如果没有查到对应的记录将返回defaultValue
     */
    public Integer queryIntOrDefault(Integer defaultValue, String sql, List<Object> args) {
        return queryIntOrDefault(defaultValue, sql, args.toArray());
    }

    /**
     * 查询并获取到一个{@link Integer}
     *
     * @param defaultValue 当没有查到结果时，默认返回值
     * @param sql          查询sql以及其参数
     * @return 如果没有查到对应的记录将返回defaultValue
     */
    public Integer queryIntOrDefault(Integer defaultValue, SqlBuilder sql) {
        return queryIntOrDefault(defaultValue, sql.sql.toString(), sql.args);
    }

    /**
     * 查询并获取到一个{@link Integer}
     *
     * @param defaultValue 当没有查到结果时，默认返回值
     * @param sql          查询sql以及其参数
     * @return 如果没有查到对应的记录将返回defaultValue
     */
    public Integer queryIntOrDefault(Integer defaultValue, String prefixSql, SqlBuilder sql) {
        return queryIntOrDefault(defaultValue, newSqlBuilder(prefixSql).add(sql));
    }

    /**
     * 查询并获取到一个{@link Long}
     *
     * @param sql  查询sql
     * @param args 查询sql语句中的?参数的值
     * @return 如果没有查到对应的记录将返回null
     */
    public Long queryLong(String sql, Object... args) {
        return queryOne(Long.class, sql, args);
    }

    /**
     * 查询并获取到一个{@link Long}
     *
     * @param sql  查询sql
     * @param args 查询sql语句中的?参数的值
     * @return 如果没有查到对应的记录将返回null
     */
    public Long queryLong(String sql, List<Object> args) {
        return queryLong(sql, args.toArray());
    }

    /**
     * 查询并获取到一个{@link Long}
     *
     * @param sql 查询sql以及其参数
     * @return 如果没有查到对应的记录将返回null
     */
    public Long queryLong(SqlBuilder sql) {
        return queryLong(sql.sql.toString(), sql.args);
    }

    /**
     * 查询并获取到一个{@link Long}
     *
     * @param sql 查询sql以及其参数
     * @return 如果没有查到对应的记录将返回null
     */
    public Long queryLong(String prefixSql, SqlBuilder sql) {
        return queryLong(newSqlBuilder(prefixSql).add(sql));
    }

    /**
     * 查询并获取到一个{@link Long}
     *
     * @param defaultValue 当没有查到结果时，默认返回值
     * @param sql          查询sql
     * @param args         查询sql语句中的?参数的值
     * @return 如果没有查到对应的记录将返回defaultValue
     */
    public Long queryLongOrDefault(Long defaultValue, String sql, Object... args) {
        return queryOneOrDefault(defaultValue, Long.class, sql, args);
    }

    /**
     * 查询并获取到一个{@link Long}
     *
     * @param defaultValue 当没有查到结果时，默认返回值
     * @param sql          查询sql
     * @param args         查询sql语句中的?参数的值
     * @return 如果没有查到对应的记录将返回defaultValue
     */
    public Long queryLongOrDefault(Long defaultValue, String sql, List<Object> args) {
        return queryLongOrDefault(defaultValue, sql, args.toArray());
    }

    /**
     * 查询并获取到一个{@link Long}
     *
     * @param defaultValue 当没有查到结果时，默认返回值
     * @param sql          查询sql以及其参数
     * @return 如果没有查到对应的记录将返回defaultValue
     */
    public Long queryLongOrDefault(Long defaultValue, SqlBuilder sql) {
        return queryLongOrDefault(defaultValue, sql.sql.toString(), sql.args);
    }

    /**
     * 查询并获取到一个{@link Long}
     *
     * @param defaultValue 当没有查到结果时，默认返回值
     * @param sql          查询sql以及其参数
     * @return 如果没有查到对应的记录将返回defaultValue
     */
    public Long queryLongOrDefault(Long defaultValue, String prefixSql, SqlBuilder sql) {
        return queryLongOrDefault(defaultValue, newSqlBuilder(prefixSql).add(sql));
    }

    /**
     * 查询并获取到一个{@link Float}
     *
     * @param sql  查询sql
     * @param args 查询sql语句中的?参数的值
     * @return 如果没有查到对应的记录将返回null
     */
    public Float queryFloat(String sql, Object... args) {
        return queryOne(Float.class, sql, args);
    }

    /**
     * 查询并获取到一个{@link Float}
     *
     * @param sql  查询sql
     * @param args 查询sql语句中的?参数的值
     * @return 如果没有查到对应的记录将返回null
     */
    public Float queryFloat(String sql, List<Object> args) {
        return queryFloat(sql, args.toArray());
    }

    /**
     * 查询并获取到一个{@link Float}
     *
     * @param sql 查询sql以及其参数
     * @return 如果没有查到对应的记录将返回null
     */
    public Float queryFloat(SqlBuilder sql) {
        return queryFloat(sql.sql.toString(), sql.args);
    }

    /**
     * 查询并获取到一个{@link Float}
     *
     * @param defaultValue 当没有查到结果时，默认返回值
     * @param sql          查询sql
     * @param args         查询sql语句中的?参数的值
     * @return 如果没有查到对应的记录将返回defaultValue
     */
    public Float queryFloatOrDefault(Float defaultValue, String sql, Object... args) {
        return queryOneOrDefault(defaultValue, Float.class, sql, args);
    }

    /**
     * 查询并获取到一个{@link Float}
     *
     * @param defaultValue 当没有查到结果时，默认返回值
     * @param sql          查询sql
     * @param args         查询sql语句中的?参数的值
     * @return 如果没有查到对应的记录将返回defaultValue
     */
    public Float queryFloatOrDefault(Float defaultValue, String sql, List<Object> args) {
        return queryFloatOrDefault(defaultValue, sql, args.toArray());
    }

    /**
     * 查询并获取到一个{@link Float}
     *
     * @param defaultValue 当没有查到结果时，默认返回值
     * @param sql          查询sql以及其参数
     * @return 如果没有查到对应的记录将返回defaultValue
     */
    public Float queryFloatOrDefault(Float defaultValue, SqlBuilder sql) {
        return queryFloatOrDefault(defaultValue, sql.sql.toString(), sql.args);
    }

    /**
     * 查询并获取到一个{@link Double}
     *
     * @param sql  查询sql
     * @param args 查询sql语句中的?参数的值
     * @return 如果没有查到对应的记录将返回null
     */
    public Double queryDouble(String sql, Object... args) {
        return queryOne(Double.class, sql, args);
    }

    /**
     * 查询并获取到一个{@link Double}
     *
     * @param sql  查询sql
     * @param args 查询sql语句中的?参数的值
     * @return 如果没有查到对应的记录将返回null
     */
    public Double queryDouble(String sql, List<Object> args) {
        return queryDouble(sql, args.toArray());
    }

    /**
     * 查询并获取到一个{@link Double}
     *
     * @param sql 查询sql以及其参数
     * @return 如果没有查到对应的记录将返回null
     */
    public Double queryDouble(SqlBuilder sql) {
        return queryDouble(sql.sql.toString(), sql.args);
    }

    /**
     * 查询并获取到一个{@link Double}
     *
     * @param defaultValue 当没有查到结果时，默认返回值
     * @param sql          查询sql
     * @param args         查询sql语句中的?参数的值
     * @return 如果没有查到对应的记录将返回defaultValue
     */
    public Double queryDoubleOrDefault(Double defaultValue, String sql, Object... args) {
        return queryOneOrDefault(defaultValue, Double.class, sql, args);
    }

    /**
     * 查询并获取到一个{@link Double}
     *
     * @param defaultValue 当没有查到结果时，默认返回值
     * @param sql          查询sql
     * @param args         查询sql语句中的?参数的值
     * @return 如果没有查到对应的记录将返回defaultValue
     */
    public Double queryDoubleOrDefault(Double defaultValue, String sql, List<Object> args) {
        return queryDoubleOrDefault(defaultValue, sql, args.toArray());
    }

    /**
     * 查询并获取到一个{@link Double}
     *
     * @param defaultValue 当没有查到结果时，默认返回值
     * @param sql          查询sql以及其参数
     * @return 如果没有查到对应的记录将返回defaultValue
     */
    public Double queryDoubleOrDefault(Double defaultValue, SqlBuilder sql) {
        return queryDoubleOrDefault(defaultValue, sql.sql.toString(), sql.args);
    }

    /**
     * 查询并获取到一个{@link String}
     *
     * @param sql  查询sql
     * @param args 查询sql语句中的?参数的值
     * @return 如果没有查到对应的记录将返回null
     */
    public String queryString(String sql, Object... args) {
        return queryOne(String.class, sql, args);
    }

    /**
     * 查询并获取到一个{@link String}
     *
     * @param sql  查询sql
     * @param args 查询sql语句中的?参数的值
     * @return 如果没有查到对应的记录将返回null
     */
    public String queryString(String sql, List<Object> args) {
        return queryString(sql, args.toArray());
    }

    /**
     * 查询并获取到一个{@link String}
     *
     * @param sql 查询sql以及其参数
     * @return 如果没有查到对应的记录将返回null
     */
    public String queryString(SqlBuilder sql) {
        return queryString(sql.sql.toString(), sql.args);
    }

    /**
     * 查询并获取到一个{@link String}
     *
     * @param defaultValue 当没有查到结果时，默认返回值
     * @param sql          查询sql
     * @param args         查询sql语句中的?参数的值
     * @return 如果没有查到对应的记录将返回defaultValue
     */
    public String queryStringOrDefault(String defaultValue, String sql, Object... args) {
        return queryOneOrDefault(defaultValue, String.class, sql, args);
    }

    /**
     * 查询并获取到一个{@link String}
     *
     * @param defaultValue 当没有查到结果时，默认返回值
     * @param sql          查询sql
     * @param args         查询sql语句中的?参数的值
     * @return 如果没有查到对应的记录将返回defaultValue
     */
    public String queryStringOrDefault(String defaultValue, String sql, List<Object> args) {
        return queryStringOrDefault(defaultValue, sql, args.toArray());
    }

    /**
     * 查询并获取到一个{@link String}
     *
     * @param defaultValue 当没有查到结果时，默认返回值
     * @param sql          查询sql以及其参数
     * @return 如果没有查到对应的记录将返回defaultValue
     */
    public String queryStringOrDefault(String defaultValue, SqlBuilder sql) {
        return queryStringOrDefault(defaultValue, sql.sql.toString(), sql.args);
    }

    /**
     * 查询并获取到二进制内容
     *
     * @param sql  查询sql
     * @param args 查询sql语句中的?参数的值
     * @return 如果没有查到对应的记录将返回null
     */
    public byte[] queryBin(String sql, Object... args) {
        return queryOne(byte[].class, sql, args);
    }

    /**
     * 查询并获取到二进制内容
     *
     * @param sql  查询sql
     * @param args 查询sql语句中的?参数的值
     * @return 如果没有查到对应的记录将返回null
     */
    public byte[] queryBin(String sql, List<Object> args) {
        return queryBin(sql, args.toArray());
    }

    /**
     * 查询并获取到二进制内容
     *
     * @param sql 查询sql以及其参数
     * @return 如果没有查到对应的记录将返回null
     */
    public byte[] queryBin(SqlBuilder sql) {
        return queryBin(sql.sql.toString(), sql.args);
    }

    /**
     * 查询并获取到二进制内容
     *
     * @param defaultValue 当没有查到结果时，默认返回值
     * @param sql          查询sql
     * @param args         查询sql语句中的?参数的值
     * @return 如果没有查到对应的记录将返回defaultValue
     */
    public byte[] queryBinOrDefault(byte[] defaultValue, String sql, Object... args) {
        return queryOneOrDefault(defaultValue, byte[].class, sql, args);
    }

    /**
     * 查询并获取到二进制内容
     *
     * @param defaultValue 当没有查到结果时，默认返回值
     * @param sql          查询sql
     * @param args         查询sql语句中的?参数的值
     * @return 如果没有查到对应的记录将返回defaultValue
     */
    public byte[] queryBinOrDefault(byte[] defaultValue, String sql, List<Object> args) {
        return queryBinOrDefault(defaultValue, sql, args.toArray());
    }

    /**
     * 查询并获取到二进制内容
     *
     * @param defaultValue 当没有查到结果时，默认返回值
     * @param sql          查询sql以及其参数
     * @return 如果没有查到对应的记录将返回defaultValue
     */
    public byte[] queryBinOrDefault(byte[] defaultValue, SqlBuilder sql) {
        return queryBinOrDefault(defaultValue, sql.sql.toString(), sql.args);
    }

    /**
     * 根据sql查找单个对象
     *
     * @param clazz 对象的class
     * @param sql   查询sql
     * @param args  查询sql语句中的?参数的值
     * @return 如果没有查到对应的记录将返回null
     */
    public <T> T queryOne(Class<T> clazz, String sql, Object... args) {
        List<T> list = queryList(clazz, sql, args);
        return list.isEmpty() ? null : list.get(0);
    }

    /**
     * 根据sql查找单个对象
     *
     * @param clazz 对象的class
     * @param sql   查询sql
     * @param args  查询sql语句中的?参数的值
     * @return 如果没有查到对应的记录将返回null
     */
    public <T> T queryOne(Class<T> clazz, String sql, Collection<Object> args) {
        return queryOne(clazz, sql, args.toArray());
    }

    /**
     * 根据sql查找单个对象
     *
     * @param clazz 对象的class
     * @param sql   查询sql以及其参数
     * @return 如果没有查到对应的记录将返回null
     */
    public <T> T queryOne(Class<T> clazz, SqlBuilder sql) {
        return queryOne(clazz, sql.sql.toString(), sql.args);
    }

    /**
     * 根据sql查找单个对象，系统会根据传入的clazz自动生成 select 语句（后续的 from XXX 等语句需要在sql参数中指定）
     *
     * @param clazz 对象的class
     * @param sql   查询sql
     * @param args  查询sql语句中的?参数的值
     * @return 如果没有查到对应的记录将返回null
     */
    public <T> T queryOneSelect(Class<T> clazz, String sql, Object... args) {
        List<T> list = queryList(clazz, newSqlBuilderSelect(clazz).add(sql, args));
        return list.isEmpty() ? null : list.get(0);
    }

    /**
     * 根据sql查找单个对象，系统会根据传入的clazz自动生成 select 语句（后续的 from XXX 等语句需要在sql参数中指定）
     *
     * @param clazz 对象的class
     * @param sql   查询sql
     * @param args  查询sql语句中的?参数的值
     * @return 如果没有查到对应的记录将返回null
     */
    public <T> T queryOneSelect(Class<T> clazz, String sql, Collection<Object> args) {
        return queryOneSelect(clazz, sql, args.toArray());
    }

    /**
     * 根据sql查找单个对象，系统会根据传入的clazz自动生成 select 语句（后续的 from XXX 等语句需要在sql参数中指定）
     *
     * @param clazz 对象的class
     * @param sql   查询sql以及其参数
     * @return 如果没有查到对应的记录将返回null
     */
    public <T> T queryOneSelect(Class<T> clazz, SqlBuilder sql) {
        return queryOneSelect(clazz, sql.sql.toString(), sql.args);
    }

    /**
     * 根据sql查找单个对象，系统会根据传入的clazz自动生成 select 语句（后续的 from XXX 等语句需要在sql参数中指定）
     *
     * @param tableName 表名，用于添加类似 t.id 的sql，如果传入null或空字符串就不生成表名字段
     * @param clazz     对象的class
     * @param sql       查询sql
     * @param args      查询sql语句中的?参数的值
     * @return 如果没有查到对应的记录将返回null
     */
    public <T> T queryOneSelect(String tableName, Class<T> clazz, String sql, Object... args) {
        List<T> list = queryList(clazz, newSqlBuilderSelect(tableName, clazz).add(sql, args));
        return list.isEmpty() ? null : list.get(0);
    }

    /**
     * 根据sql查找单个对象，系统会根据传入的clazz自动生成 select 语句（后续的 from XXX 等语句需要在sql参数中指定）
     *
     * @param tableName 表名，用于添加类似 t.id 的sql，如果传入null或空字符串就不生成表名字段
     * @param clazz     对象的class
     * @param sql       查询sql
     * @param args      查询sql语句中的?参数的值
     * @return 如果没有查到对应的记录将返回null
     */
    public <T> T queryOneSelect(String tableName, Class<T> clazz, String sql, Collection<Object> args) {
        return queryOneSelect(tableName, clazz, sql, args.toArray());
    }

    /**
     * 根据sql查找单个对象，系统会根据传入的clazz自动生成 select 语句（后续的 from XXX 等语句需要在sql参数中指定）
     *
     * @param tableName 表名，用于添加类似 t.id 的sql，如果传入null或空字符串就不生成表名字段
     * @param clazz     对象的class
     * @param sql       查询sql以及其参数
     * @return 如果没有查到对应的记录将返回null
     */
    public <T> T queryOneSelect(String tableName, Class<T> clazz, SqlBuilder sql) {
        return queryOneSelect(tableName, clazz, sql.sql.toString(), sql.args);
    }

    /**
     * 根据sql查找单个对象
     *
     * @param defaultValue 当没有查到结果时，默认返回值
     * @param clazz        对象的class
     * @param sql          查询sql
     * @param args         查询sql语句中的?参数的值
     * @return 如果没有查到对应的记录将返回defaultValue
     */
    public <T> T queryOneOrDefault(T defaultValue, Class<T> clazz, String sql, Object... args) {
        List<T> list = queryList(clazz, sql, args);
        if (list.isEmpty()) {
            return defaultValue;
        }
        T one = list.get(0);
        if (null == one) {
            return defaultValue;
        }
        return one;
    }

    /**
     * 根据sql查找单个对象
     *
     * @param defaultValue 当没有查到结果时，默认返回值
     * @param clazz        对象的class
     * @param sql          查询sql
     * @param args         查询sql语句中的?参数的值
     * @return 如果没有查到对应的记录将返回defaultValue
     */
    public <T> T queryOneOrDefault(T defaultValue, Class<T> clazz, String sql, Collection<Object> args) {
        return queryOneOrDefault(defaultValue, clazz, sql, args.toArray());
    }

    /**
     * 根据sql查找单个对象
     *
     * @param defaultValue 当没有查到结果时，默认返回值
     * @param clazz        对象的class
     * @param sql          查询sql以及其参数
     * @return 如果没有查到对应的记录将返回defaultValue
     */
    public <T> T queryOneOrDefault(T defaultValue, Class<T> clazz, SqlBuilder sql) {
        return queryOneOrDefault(defaultValue, clazz, sql.sql.toString(), sql.args);
    }

    /**
     * 根据sql查找单个对象，查找结果通过一个包含字段名和值的WrappedMap返回
     *
     * @param sql  查询sql
     * @param args 查询sql语句中的?参数的值
     * @return 如果没有查到对应的记录将返回null
     */
    public DbMap queryOne(String sql, Object... args) {
        List<DbMap> list = queryList(sql, args);
        return list.isEmpty() ? null : list.get(0);
    }

    /**
     * 根据sql查找单个对象，查找结果通过一个包含字段名和值的Map返回
     *
     * @param sql  查询sql
     * @param args 查询sql语句中的?参数的值
     * @return 如果没有查到对应的记录将返回null
     */
    public DbMap queryOne(String sql, Collection<Object> args) {
        return queryOne(sql, args.toArray());
    }

    /**
     * 根据sql查找单个对象，查找结果通过一个包含字段名和值的Map返回
     *
     * @param sql 查询sql以及其参数
     * @return 如果没有查到对应的记录将返回null
     */
    public DbMap queryOne(SqlBuilder sql) {
        return queryOne(sql.sql.toString(), sql.args);
    }

    /**
     * 根据sql查找单个对象，查找结果通过一个包含字段名和值的WrappedMap返回
     *
     * @param defaultValue 当没有查到结果时，默认返回值
     * @param sql          查询sql
     * @param args         查询sql语句中的?参数的值
     * @return 如果没有查到对应的记录将返回defaultValue
     */
    public DbMap queryOneOrDefault(DbMap defaultValue, String sql, Object... args) {
        List<DbMap> list = queryList(sql, args);
        if (list.isEmpty()) {
            return null;
        }
        DbMap one = list.get(0);
        if (null == one) {
            return defaultValue;
        }
        return one;
    }

    /**
     * 根据sql查找单个对象，查找结果通过一个包含字段名和值的Map返回
     *
     * @param defaultValue 当没有查到结果时，默认返回值
     * @param sql          查询sql
     * @param args         查询sql语句中的?参数的值
     * @return 如果没有查到对应的记录将返回defaultValue
     */
    public DbMap queryOneOrDefault(DbMap defaultValue, String sql, Collection<Object> args) {
        return queryOneOrDefault(defaultValue, sql, args.toArray());
    }

    /**
     * 根据sql查找单个对象，查找结果通过一个包含字段名和值的Map返回
     *
     * @param defaultValue 当没有查到结果时，默认返回值
     * @param sql          查询sql以及其参数
     * @return 如果没有查到对应的记录将返回defaultValue
     */
    public DbMap queryOneOrDefault(DbMap defaultValue, SqlBuilder sql) {
        return queryOneOrDefault(defaultValue, sql.sql.toString(), sql.args);
    }

    /**
     * 查询一个列表的内容
     *
     * @param clazz 目标bean的class
     * @param sql   查询sql
     * @param args  查询sql语句中的?参数的值
     * @return 如果没有查到对应的记录将返回空的List
     */
    public <T> List<T> queryList(Class<T> clazz, String sql, Object... args) {
        int rows = 0;
        String result = null;
        Throwable ex = null;
        String logString = null;
        long start = System.currentTimeMillis();
        try {
            RowMapper<T> rowMapper = JdbcRowMappers.getMapper(clazz);
            List<T> list = jdbcTemplate.query(new SqlCreator(sql, args, dialect), rowMapper);
            rows = list.size();
            if (rows == 1) { // 如果结果只有1列并且属于可以输出的就直接把结果输出
                if (DbMap.isPrimitiveWrapperType(clazz)) {
                    result = String.valueOf(list.get(0));
                } else if (Date.class.isAssignableFrom(clazz)) {
                    result = DatetimeUtils.format((Date) list.get(0));
                }
            }
            return list;
        } catch (Throwable e) {
            ex = e;
            logString = buildLog(sql, args);
            if (ex instanceof BeanInstantiationException) {
                ex = new IllegalArgumentException("Please add a constructor without parameters to the class " + clazz.getName() + ", or the instance cannot be created");
            }
            throw new SqlExecuteException(logString, e);
        } finally {
            if (log.isDebugEnabled()) {
                if (null == ex) {
                    if (rows == 1 && null != result) {
                        logString = buildLog(sql, args) + " -- " + (System.currentTimeMillis() - start) + "ms-result: " + result;
                    } else {
                        logString = buildLog(sql, args) + " -- " + (System.currentTimeMillis() - start) + "ms-rows: " + rows;
                    }
                    log.debug(logString);
                } else {
                    logString += " -- " + StackTraceUtils.printThrowable(ex);
                    log.error(logString);
                }
            }
        }
    }

    /**
     * 查询一个列表的内容
     *
     * @param clazz 目标bean的class
     * @param sql   查询sql
     * @param args  查询sql语句中的?参数的值
     * @return 如果没有查到对应的记录将返回空的List
     */
    public <T> List<T> queryList(Class<T> clazz, String sql, Collection<Object> args) {
        return queryList(clazz, sql, args.toArray());
    }

    /**
     * 查询一个列表的内容
     *
     * @param clazz 目标bean的class
     * @param sql   查询sql以及其参数
     * @return 如果没有查到对应的记录将返回空的List
     */
    public <T> List<T> queryList(Class<T> clazz, SqlBuilder sql) {
        return queryList(clazz, sql.sql.toString(), sql.args);
    }

    /**
     * 查询一个列表的内容，系统会根据传入的clazz自动生成 select 语句（后续的 from XXX 等语句需要在sql参数中指定）
     *
     * @param clazz 目标bean的class
     * @param sql   查询sql
     * @param args  查询sql语句中的?参数的值
     * @return 如果没有查到对应的记录将返回空的List
     */
    public <T> List<T> queryListSelect(Class<T> clazz, String sql, Object... args) {
        return queryList(clazz, newSqlBuilderSelect(clazz).add(sql, args));
    }

    /**
     * 查询一个列表的内容，系统会根据传入的clazz自动生成 select 语句（后续的 from XXX 等语句需要在sql参数中指定）
     *
     * @param clazz 目标bean的class
     * @param sql   查询sql
     * @param args  查询sql语句中的?参数的值
     * @return 如果没有查到对应的记录将返回空的List
     */
    public <T> List<T> queryListSelect(Class<T> clazz, String sql, Collection<Object> args) {
        return queryList(clazz, newSqlBuilderSelect(clazz).add(sql, args.toArray()));
    }

    /**
     * 查询一个列表的内容，系统会根据传入的clazz自动生成 select 语句（后续的 from XXX 等语句需要在sql参数中指定）
     *
     * @param clazz 目标bean的class
     * @param sql   查询sql以及其参数
     * @return 如果没有查到对应的记录将返回空的List
     */
    public <T> List<T> queryListSelect(Class<T> clazz, SqlBuilder sql) {
        return queryList(clazz, newSqlBuilderSelect(clazz).add(sql));
    }

    /**
     * 查询一个列表的内容，系统会根据传入的clazz自动生成 select 语句（后续的 from XXX 等语句需要在sql参数中指定）
     *
     * @param tableName 表名，用于添加类似 t.id 的sql，如果传入null或空字符串就不生成表名字段
     * @param clazz     目标bean的class
     * @param sql       查询sql
     * @param args      查询sql语句中的?参数的值
     * @return 如果没有查到对应的记录将返回空的List
     */
    public <T> List<T> queryListSelect(String tableName, Class<T> clazz, String sql, Object... args) {
        return queryList(clazz, newSqlBuilderSelect(tableName, clazz).add(sql, args));
    }

    /**
     * 查询一个列表的内容，系统会根据传入的clazz自动生成 select 语句（后续的 from XXX 等语句需要在sql参数中指定）
     *
     * @param tableName 表名，用于添加类似 t.id 的sql，如果传入null或空字符串就不生成表名字段
     * @param clazz     目标bean的class
     * @param sql       查询sql
     * @param args      查询sql语句中的?参数的值
     * @return 如果没有查到对应的记录将返回空的List
     */
    public <T> List<T> queryListSelect(String tableName, Class<T> clazz, String sql, Collection<Object> args) {
        return queryList(clazz, newSqlBuilderSelect(tableName, clazz).add(sql, args.toArray()));
    }

    /**
     * 查询一个列表的内容，系统会根据传入的clazz自动生成 select 语句（后续的 from XXX 等语句需要在sql参数中指定）
     *
     * @param tableName 表名，用于添加类似 t.id 的sql，如果传入null或空字符串就不生成表名字段
     * @param clazz     目标bean的class
     * @param sql       查询sql以及其参数
     * @return 如果没有查到对应的记录将返回空的List
     */
    public <T> List<T> queryListSelect(String tableName, Class<T> clazz, SqlBuilder sql) {
        return queryList(clazz, newSqlBuilderSelect(tableName, clazz).add(sql));
    }

    /**
     * 查询一个列表的内容，返回一个包含各字段名和值的{@link DbMap}的列表
     *
     * @param sql  查询sql
     * @param args 查询sql语句中的?参数的值
     * @return 如果没有查到对应的记录将返回空的List
     */
    public List<DbMap> queryList(String sql, Object... args) {
        int rows = 0;
        Throwable ex = null;
        String logString = null;
        long start = System.currentTimeMillis();
        try {
            List<DbMap> list = jdbcTemplate.query(new SqlCreator(sql, args, dialect), new RowMapperResultSetExtractor<>(new ColumnJdbcMapRowMapper()));
            if (null == list) {
                return new ArrayList<>();
            }
            rows = list.size();
            return list;
        } catch (Throwable e) {
            ex = e;
            logString = buildLog(sql, args);
            throw new SqlExecuteException(logString, e);
        } finally {
            if (log.isDebugEnabled()) {
                if (null == ex) {
                    logString = buildLog(sql, args) + " -- " + (System.currentTimeMillis() - start) + "ms-rows: " + rows;
                    log.debug(logString);
                } else {
                    logString += " -- " + StackTraceUtils.printThrowable(ex);
                    log.error(logString);
                }
            }
        }
    }

    /**
     * 查询一个列表的内容，返回一个包含各字段名和值的Map的列表
     *
     * @param sql  查询sql
     * @param args 查询sql语句中的?参数的值
     * @return 如果没有查到对应的记录将返回空的List
     */
    public List<DbMap> queryList(String sql, Collection<Object> args) {
        return queryList(sql, args.toArray());
    }

    /**
     * 查询一个列表的内容，返回一个包含各字段名和值的Map的列表
     *
     * @param sql 查询sql以及其参数
     * @return 如果没有查到对应的记录将返回空的List
     */
    public List<DbMap> queryList(SqlBuilder sql) {
        return queryList(sql.sql.toString(), sql.args);
    }

    /**
     * 查询一页的内容
     *
     * @param clazz 目标bean的class
     * @param sql   查询sql
     * @param args  查询sql语句中的?参数的值
     * @return 如果没有查到对应的记录将返回空的List
     */
    public <T> List<T> queryPage(Class<T> clazz, long pageNo, long pageSize, String sql, Object... args) {
        long startIndex = (pageNo - 1) * pageSize;
        startIndex = Math.max(startIndex, 0);
        String querySql;
        if (JdbcDialectEnum.MySQL.equals(dialect)) {
            querySql = sql + " limit " + startIndex + ", " + pageSize;
        } else {
            querySql = sql + " limit " + pageSize + " offset " + startIndex;
        }
        return queryList(clazz, querySql, args);
    }

    /**
     * 查询一页的内容
     *
     * @param clazz 目标bean的class
     * @param sql   查询sql
     * @param args  查询sql语句中的?参数的值
     * @return 如果没有查到对应的记录将返回空的List
     */
    public <T> List<T> queryPage(Class<T> clazz, long pageNo, long pageSize, String sql, Collection<Object> args) {
        return queryPage(clazz, pageNo, pageSize, sql, args.toArray());
    }

    /**
     * 查询一页的内容
     *
     * @param clazz 目标bean的class
     * @param sql   查询sql以及其参数
     * @return 如果没有查到对应的记录将返回空的List
     */
    public <T> List<T> queryPage(Class<T> clazz, long pageNo, long pageSize, SqlBuilder sql) {
        return queryPage(clazz, pageNo, pageSize, sql.sql.toString(), sql.args);
    }

    /**
     * 查询一页的内容
     *
     * @param clazz 目标bean的class
     * @param sql   查询sql以及其参数
     * @return 如果没有查到对应的记录将返回空的List
     */
    public <T> List<T> queryPage(Class<T> clazz, long pageNo, long pageSize, String prefixSql, SqlBuilder sql) {
        return queryPage(clazz, pageNo, pageSize, newSqlBuilder(prefixSql).add(sql));
    }

    /**
     * 查询一页的内容
     *
     * @param clazz 目标bean的class
     * @param sql   查询sql以及其参数
     * @return 如果没有查到对应的记录将返回空的List
     */
    public <T> List<T> queryPage(Class<T> clazz, long pageNo, long pageSize, String prefixSql, SqlBuilder sql, String suffixSql, Object... args) {
        return queryPage(clazz, pageNo, pageSize, newSqlBuilder(prefixSql).add(sql).add(suffixSql, args));
    }

    /**
     * 查询一页的内容，系统会根据传入的clazz自动生成 select 语句
     *
     * @param clazz 目标bean的class
     * @param sql   查询sql以及其参数
     * @return 如果没有查到对应的记录将返回空的List
     */
    public <T> List<T> queryPageSelect(Class<T> clazz, long pageNo, long pageSize, SqlBuilder sql) {
        return queryPage(clazz, pageNo, pageSize, newSqlBuilderSelect(clazz).add(sql));
    }

    /**
     * 查询一页的内容，系统会根据传入的clazz自动生成 select 语句
     *
     * @param tableName 表名，用于添加类似 t.id 的sql，如果传入null或空字符串就不生成表名字段
     * @param clazz     目标bean的class
     * @param sql       查询sql以及其参数
     * @return 如果没有查到对应的记录将返回空的List
     */
    public <T> List<T> queryPageSelect(String tableName, Class<T> clazz, long pageNo, long pageSize, SqlBuilder sql) {
        return queryPage(clazz, pageNo, pageSize, newSqlBuilderSelect(tableName, clazz).add(sql));
    }

    /**
     * 查询一页的内容，系统会根据传入的clazz自动生成 select 语句（后续的 from XXX 等语句需要在sql参数中指定）
     *
     * @param clazz 目标bean的class
     * @param sql   查询sql以及其参数
     * @return 如果没有查到对应的记录将返回空的List
     */
    public <T> List<T> queryPageSelect(Class<T> clazz, long pageNo, long pageSize, SqlBuilder sql, String suffixSql, Object... args) {
        return queryPage(clazz, pageNo, pageSize, newSqlBuilderSelect(clazz).add(sql).add(suffixSql, args));
    }

    /**
     * 查询一页的内容，系统会根据传入的clazz自动生成 select 语句（后续的 from XXX 等语句需要在sql参数中指定）
     *
     * @param tableName 表名，用于添加类似 t.id 的sql，如果传入null或空字符串就不生成表名字段
     * @param clazz     目标bean的class
     * @param sql       查询sql以及其参数
     * @return 如果没有查到对应的记录将返回空的List
     */
    public <T> List<T> queryPageSelect(String tableName, Class<T> clazz, long pageNo, long pageSize, SqlBuilder sql, String suffixSql, Object... args) {
        return queryPage(clazz, pageNo, pageSize, newSqlBuilderSelect(tableName, clazz).add(sql).add(suffixSql, args));
    }

    /**
     * 合并带?的SQL语句，用于调试时显示执行的SQL之用
     */
    public static String buildLog(String sql, Object... args) {
        if (null == args || args.length == 0) {
            return sql;
        }
        String[] split = (sql + " ").split("\\?");
        if (args.length == split.length - 1) {
            StringBuilder log = new StringBuilder();
            int i;
            for (i = 0; i < args.length; i++) {
                log.append(split[i]);
                if (args[i] instanceof String || args[i] instanceof Enum) {
                    // 参数有限制，防止显示在日志中的SQL出现换行或者太长的情况
                    String src = args[i].toString().replace("'", "\\'").replace("\r", "").replace("\n", "").trim();
                    String append = src;
                    if (src.length() > 120) {
                        append = src.substring(0, 50) + "...(" + args[i].toString().length() + ")..." + src.substring(src.length() - 50);
                    }
                    log.append("'").append(append).append("'");
                } else if (args[i] instanceof Date) {
                    log.append("'").append(DatetimeUtils.format(((Date) args[i]))).append("'");
                } else if (args[i] instanceof byte[]) { // 二进制内容不显示，只显示长度信息
                    log.append("[Binary Content ").append(((byte[]) args[i]).length).append("]");
                } else {
                    log.append(args[i]);
                }
            }
            log.append(split[i]);
            log.deleteCharAt(log.length() - 1);
            return log.toString();
        }
        return "";
    }

    public static class SqlCreator implements PreparedStatementCreator {

        protected final String sql;

        protected Object[] args;

        protected JdbcDialectEnum dialect;

        public SqlCreator(String sql, Object[] args, JdbcDialectEnum dialect) {
            this.sql = sql;
            this.args = args;
            this.dialect = dialect;
        }

        protected PreparedStatement create(Connection con) throws SQLException {
            return con.prepareStatement(sql);
        }

        @NonNull
        @Override
        public PreparedStatement createPreparedStatement(@NonNull Connection con) throws SQLException {
            PreparedStatement ps = create(con);
            if (args != null) {
                int i = 1;
                if (JdbcDialectEnum.SQLite.equals(dialect)) {
                    // SQLite的时间类型需要转换成字符串才能正确完成查询
                    for (Object arg : args) {
                        if (arg instanceof Timestamp) {
                            ps.setString(i++, DatetimeUtils.format(((Timestamp) arg).getTime(), DatetimeUtils.DF_yyyy_MM_dd_HHmmss));
                        } else if (arg instanceof Date) {
                            ps.setString(i++, DatetimeUtils.format(((Date) arg).getTime(), DatetimeUtils.DF_yyyy_MM_dd_HHmmss));
                        } else if (arg instanceof Enum) {
                            ps.setString(i++, arg.toString());
                        } else {
                            ps.setObject(i++, arg);
                        }
                    }
                } else {
                    for (Object arg : args) {
                        if (arg instanceof Timestamp) {
                            ps.setTimestamp(i++, (Timestamp) arg);
                        } else if (arg instanceof Date) {
                            ps.setTimestamp(i++, new Timestamp(((Date) arg).getTime()));
                        } else if (arg instanceof Enum) {
                            ps.setString(i++, arg.toString());
                        } else {
                            ps.setObject(i++, arg);
                        }
                    }
                }
            }
            return ps;
        }
    }

    public static class SqlCreatorForInsert extends SqlCreator {

        public SqlCreatorForInsert(String sql, Object[] args, JdbcDialectEnum dialect) {
            super(sql, args, dialect);
        }

        @Override
        protected PreparedStatement create(Connection con) throws SQLException {
            return con.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
        }
    }
}
